/*
 * util.cpp
 *
 *  Created on: Sep 26, 2013
 *      Author: killerg
 */

#include "util.h"

#include <unistd.h>
#include <stdarg.h>
#include <event2/http.h>
#include <assert.h>
#include <libgen.h>
#include <fcntl.h>

char get_proc_status(int pid) {
	std::string path = fmt_cstr("/proc/%d/status", pid);

	FILE* f = fopen(path.c_str(), "r");
	if (f == NULL) {
		return 'N';
	}

	char* str = NULL;
	size_t len;
	if (getline(&str, &len, f) >= 0) {
		free(str);
		str = NULL;
	}

	char status = 'N';
	if (getline(&str, &len, f) >= 0) {
		if (sscanf(str, "State: %c", &status) != 1) {
			status = 'N';
		}
		free(str);
		str = NULL;
	}
	fclose(f);
	return status;
}

// 取得路径
const char* get_path() {
	static std::string path;
	if (!path.empty())
		return path.c_str();

	char buf[1024] = { 0 };

	int rslt = readlink("/proc/self/exe", buf, 1024);
	if (rslt < 0 || rslt >= 1024)
		return NULL;

	buf[rslt] = '\0';
	path = buf;
	return path.c_str();
}

const char* get_thisdir() {
	static std::string path;
	if (!path.empty()) {
		return path.c_str();
	}

	std::string tmp = get_path();
	path = dirname((char*) tmp.c_str());
	return path.c_str();
}

const char* get_deploy() {
	static std::string path;
	if (!path.empty()) {
		return path.c_str();
	}

	std::string tmp = get_path();
	path = dirname(dirname((char*) tmp.c_str()));
	return path.c_str();
}

const int BUFFSIZE = 1024 * 8;
// 转换成字符串，长度不超过1024
std::string fmt_str(const char* fmt, ...) {
	va_list p;
	va_start(p, fmt);

	static char buf[BUFFSIZE] = { 0 };
	vsprintf(buf, fmt, p);

	va_end(p);
	return std::move(std::string(buf));
}

// 转换成字符串，长度不超过1024
const char* fmt_cstr(const char* fmt, ...) {
	va_list p;
	va_start(p, fmt);

	static char buf[BUFFSIZE] = { 0 };
	vsprintf(buf, fmt, p);

	va_end(p);
	return buf;
}

const char* to_cstr(const Json::Value &json) {
	static std::string tmp;
	tmp = to_str(json);
	return tmp.c_str();
}

std::string to_str(const Json::Value &json) {
	static Json::FastWriter writer;
	return std::move(writer.write(json));
}

Json::Value to_json(const char* str) {
	if (str == NULL || 0 == strcmp(str, "")) {
		return Json::nullValue;
	}

	Json::Value json;
	Json::Reader reader;
	bool ok = reader.parse(str, json);
	if (!ok) {
		UTIL_LOG(E, "<<<<<<<<<<<<<<<<<<<<<<invalid json----------------");
		UTIL_LOG(E, "%s", str);
		UTIL_LOG(E, "----------------------invalid json>>>>>>>>>>>>>>>>");
	}

	return std::move(json);
}

Json::Value to_json(const std::string &str) {
	if (str.empty()) {
		return Json::nullValue;
	}
	Json::Value json;
	Json::Reader reader;
	bool ok = reader.parse(str, json);
	if (!ok) {
		UTIL_LOG(E, "<<<<<<<<<<<<<<<<<<<<<<invalid json----------------");
		UTIL_LOG(E, "%s", str.c_str());
		UTIL_LOG(E, "----------------------invalid json>>>>>>>>>>>>>>>>");
		return Json::nullValue;
	}

	return std::move(json);
}

#include <fstream>
// 读取文件中读取json
const char* load_file(const char* path) {
	static const int FILEBUFFSIZE = 1024 * 1024 * 32;
	static char buffer[FILEBUFFSIZE];

	FILE *wfp = fopen(path, "r");
	if (wfp == NULL) {
		perror(path);
		return "";
	}

	int count = fread(buffer, 1, FILEBUFFSIZE, wfp);
	fclose(wfp);
	buffer[count] = 0;
	return buffer;
}

#include <string.h>
bool save_file(const char* path, const char* data) {
	FILE *wfp = fopen(path, "w");
	if (NULL == wfp)
		return false;

	int len = strlen(data);
	int wlen = fwrite(data, 1, len, wfp);
	if (fflush(wfp) != 0)
		perror(path);
	fclose(wfp);

	return wlen == len;
}

// void split(str, delim, list)
void split(const std::string &str, const std::string &delimiter,
		std::vector<std::string> &dest) {
	dest.clear();

	if (str.empty())
		return;

	size_t pos = 0, found = 0;
	std::string tmp = str + delimiter;
	while (found != std::string::npos) {
		found = tmp.find(delimiter, pos);
		if (found != std::string::npos)
			dest.push_back(std::string(tmp, pos, found - pos));
		pos = found + 1;
	}
}

const char* encode(const char* data) {
	static std::string buff;
	char* encoded = evhttp_encode_uri(data);
	buff = encoded;
	free(encoded);
	return buff.c_str();
}

const char* decode(const char* data) {
	static std::string buff;
	char* decoded = evhttp_decode_uri(data);
	buff = decoded;
	free(decoded);
	return buff.c_str();
}

const unsigned char utf8_look_for_table[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
		2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3,
		3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5,
		5, 6, 6, 1, 1 };

#include <sys/mman.h>
#include <sys/stat.h>        /* For mode constants */
#include <fcntl.h>           /* For O_* constants */
void* shm_get(const char* path, size_t size) {
	if (strlen(path) <= 1 || path[0] != '/' || size <= 0)
		return NULL;
	//int fd = open("hello", O_RDWR | O_CREAT, S_IRWXU);
	int fd = shm_open(path, O_RDWR, S_IRWXU);
	if (fd < 0) {
		return NULL;
	}
	int page_size = sysconf(_SC_PAGE_SIZE);
	ftruncate(fd, size);

	void* data = (void*) mmap(0, size, PROT_WRITE | PROT_READ, MAP_SHARED, fd,
			0);
	if (data == MAP_FAILED || data == NULL) {
		return NULL;
	}
	close(fd);
	return data;
}

void* shm_new(const char* path, size_t size) {
	if (strlen(path) <= 1 || path[0] != '/' || size <= 0)
		return NULL;
	//int fd = open("hello", O_RDWR | O_CREAT, S_IRWXU);
	int fd = shm_open(path, O_RDWR | O_CREAT | O_EXCL, S_IRWXU);
	if (fd < 0) {
		perror("shm_open");
		return NULL;
	}
	int page_size = sysconf(_SC_PAGE_SIZE);
	ftruncate(fd, size);

	void* data = (void*) mmap(0, size, PROT_WRITE | PROT_READ, MAP_SHARED, fd,
			0);
	if (data == MAP_FAILED || data == NULL) {
		perror("mmap");
		return NULL;
	}
	close(fd);
	return data;
}
void shm_free(const char* path, size_t size, void* data) {
	if (strlen(path) <= 1 || path[0] != '/' || size <= 0 || data == NULL)
		return;
	munmap(data, size);
	//unlink("hello");
	if (shm_unlink(path) < 0) {
		perror("shm_unlink");
		return;
	}
}

log_cb_t util_logcb = NULL;
vlog_cb_t util_vlogcb = NULL;

static const char* STATUS_NAME[] =
		{ "Noexist", "Initing", "Running", "Shutting", };

static int writelogv(int fd, const char* fmt, va_list arg) {
	static char buffer[2048];
	static char myfmt[1024];
	time_t now = time(NULL);
//	strftime(buffer,100,"%Y-%m-%d_%H:%M:%S",localtime(&now));

	sprintf(myfmt, "%s\n", fmt);
	int len = vsprintf(buffer, myfmt, arg);
	int pos = 0;
	do {
		int res = write(fd, buffer + pos, len - pos);
		if (res < 0)
			break;

		pos += res;
	} while (pos < len);

	return len;
}

static int writelog(int fd, const char* fmt, ...) {
	va_list arg;
	va_start(arg, fmt);
	int n = writelogv(fd, fmt, arg);
	va_end(arg);
	return n;
}

static void out_data(int fd, SvcData* data) {
	static char buffer[100];
	time_t now = time(NULL);
	strftime(buffer, 100, "%Y-%m-%d_%H:%M:%S", localtime(&data->refresh));

	writelog(fd, "%s %d %d %s", STATUS_NAME[data->status], data->pid,
			data->shut, buffer);
}

bool svc_ctrl(int argc, char** argv, run_cb_t run_cb) {

	bool back = false;
	bool shut = false;
	bool query = false;

	const char* prefix = "";
	int opt;
	while ((opt = getopt(argc, argv, "bsq")) != -1) {
		switch (opt) {
		case 'b':
			back = true;
			break;
		case 's':
			shut = true;
			break;
		case 'q':
			query = true;
			break;
		}
	}
	if (argc != optind)
		prefix = argv[optind];

	SvcData none;
	none.pid = -1;
	none.refresh = -1;
	none.shut = true;
	none.status = SvcData::Noexist;
	strncpy(none.prefix, prefix, 32);

	std::string name = fmt_cstr("/%s_%s", basename(argv[0]), prefix);
	SvcData* data = (SvcData*) shm_get(name.c_str(), sizeof(SvcData));

	if (shut) {
		if (!data) {
			out_data(1, &none);
			return true;
		}
		data->shut = true;
		return true;
	} else if (query) {
		if (!data) {
			out_data(1, &none);
			return true;
		}
		if (get_proc_status(data->pid) == 'N') {
			shm_free(name.c_str(), sizeof(SvcData), data);
			out_data(1, &none);
			return true;
		}

		out_data(1, data);
	} else {
		// exist
		if (data) {
			if (get_proc_status(data->pid) != 'N') {
				printf("already exist\n");
				return true;
			}
			shm_free(name.c_str(), sizeof(SvcData), data);
		}

		if (!run_cb) {
			return true;
		}

		if (back) {
			if (daemon(1, 0) < 0) {
				perror("daemon");
				return true;
			}
		}

		data = (SvcData*) shm_new(name.c_str(), sizeof(SvcData));
		data->pid = getpid();
		data->shut = false;
		data->refresh = time(NULL);
		strncpy(data->prefix, prefix, 32);

		run_cb(data);

		shm_free(name.c_str(), sizeof(SvcData), data);
		data = NULL;
		return true;
	}

	return true;
}

std::string execute(const char* cmd) {
	std::string str;

	FILE* read = popen(cmd, "r");
	assert(read);

	char* buf = NULL;
	size_t size = 0;
	while (getline(&buf, &size, read) > 0) {
		str = buf;
		free(buf);
	}
	pclose(read);
	return str;
}
